﻿#light

module PricingRules.ShareSpread

open System

type Price = { Bid : double; Ask : double }

let Spread price = price.Ask - price.Bid 

let Mid price = (price.Bid + price.Ask) / 2.0

type SpreadBand = { Limit : double; Points : double }

type Spread = 
    | Percent of double
    | PointsBands of SpreadBand list

// find the right band from the list
let SpreadBandPoints spreads mid =
    let band = List.find (fun band -> mid < band.Limit) spreads
    band.Points
    
let ClientSpreadPercent percent clientMid = (1.0+percent) * clientMid

// get the fix or banded spread for the client
let ClientSpread spread clientMid =
    match spread with
    | Percent spread -> ClientSpreadPercent spread clientMid
    | PointsBands spread -> SpreadBandPoints spread clientMid
    
type ShareRuleParams = {
    Spread : Spread
    Dividend : double
    InterestRate : double
    Expiry : DateTime
    DecimalPlaces : int
    MaxSpread : double
    MaxSpreadTriggerPrice : double
}

type PriceRuleParams = 
    | ShareRuleParams

// shares pricing rule    
let DayToMaturity expiry = DateTime.op_Subtraction(expiry, DateTime.Now).TotalDays

let DailyInterestRate rate expiry = rate * ((DayToMaturity expiry) / 365.0)

let SharesClientMid feed (parms:ShareRuleParams) = 
    ((Mid feed) * (1.0 + DailyInterestRate parms.InterestRate parms.Expiry)) - parms.Dividend

let ClientPrice mid spread decimalPlaces = 
    let halfSpread = spread / 2.0
    { 
        Bid = Math.Round(mid - halfSpread, decimalPlaces, MidpointRounding.AwayFromZero); 
        Ask = Math.Round(mid + halfSpread, decimalPlaces, MidpointRounding.AwayFromZero); 
    }

let PriceSharesRule feed parms = 
    let clientMid = SharesClientMid feed parms
    let clientSpread = ((Spread feed) + (ClientSpread parms.Spread clientMid)) 
    ClientPrice clientMid clientSpread parms.DecimalPlaces      

let CappedPercentSpread percentSpread clientMid maxSpread maxSpreadMid =
    let spread = ClientSpreadPercent percentSpread clientMid
    match spread with
    | s when s >= maxSpread && clientMid >= maxSpreadMid -> maxSpread
    | _ -> spread
    
let CappedClientSpread spread clientMid maxSpread maxSpreadMid =
    match spread with
    | Percent spread -> CappedPercentSpread spread clientMid maxSpread maxSpreadMid 
    | PointsBands spread -> SpreadBandPoints spread clientMid

let CappedSpreadRule feed parms =
    let clientMid = SharesClientMid feed parms
    let clientSpread = ((Spread feed) + (CappedClientSpread parms.Spread clientMid parms.MaxSpread parms.MaxSpreadTriggerPrice)) 
    ClientPrice clientMid clientSpread parms.DecimalPlaces      
  
// test share pricing
let vod = { Bid = 98.0; Ask = 99.2; }
let vodParms =  {
    Spread = PointsBands [ { Limit=50.0; Points=2.0;}; { Limit=100.0; Points=3.0}; { Limit=10000.0; Points=4.0} ]
    //Spread= Percent 0.02
    Dividend = 0.0
    InterestRate = 0.05
    Expiry = new DateTime(2010, 9, 25)
    DecimalPlaces = 3
    MaxSpread = 1.0
    MaxSpreadTriggerPrice = 50.0
}

let price = CappedSpreadRule vod vodParms

printfn "our price = %f/%f" price.Bid price.Ask

let key = Console.ReadKey()